﻿Imports System.Linq.Expressions
Imports System.Reflection
Imports System.Runtime.CompilerServices

Public Module Extensions
	Public Function GetMemberInfo(Of TObject, TProperty)(expression As Expression(Of Func(Of TObject, TProperty))) As MemberInfo
		Dim member = TryCast(expression.Body, MemberExpression)
		If member IsNot Nothing Then
			Return member.Member
		End If

		Throw New ArgumentException("expression")
	End Function

	<Extension()>
	Public Function FromUTCToLocalTime(utcTime As Date) As Date
		Return DateTime.SpecifyKind(utcTime, DateTimeKind.Utc).ToLocalTime
	End Function

	<Extension()>
	Public Function MonthsDifference(startDate As Date, endDate As Date) As Integer
		Return (startDate.Month - endDate.Month) + 12 * (startDate.Year - endDate.Year)
	End Function

	<Extension()>
	Public Function FirstOfTheMonth(myDate As Date) As Date
		Return New Date(myDate.Year, myDate.Month, 1)
	End Function

	<Extension()>
	Public Sub SelectAll(myNumericUpDown As NumericUpDown)
		myNumericUpDown.Select(0, myNumericUpDown.Text.Length)
	End Sub

	<Extension()>
	Public Function UtcCompare(firstDate As DateTime, secondDate As DateTime) As Integer
		Return DateTime.Compare(firstDate.ToUniversalTime, secondDate.ToUniversalTime)
	End Function

	<Extension()>
	Public Function MakeSafeFileName(FileName As String) As String
		Return FileName.Where(Function(x) Not IO.Path.GetInvalidFileNameChars.Contains(x)).ToArray
	End Function

	<Extension()>
	Public Function Median(Of TColl, TValue)(source As IEnumerable(Of TColl), selector As Func(Of TColl, TValue)) As Decimal
		Return source.Select(selector).Median()
	End Function

	<Extension()>
	Function Median(Of T)(source As IEnumerable(Of T)) As Decimal
		source = source.Where(Function(x) x IsNot Nothing)

		Dim count As Integer = source.Count()
		If count = 0 Then
			Throw New InvalidOperationException("Cannot compute median for an empty set.")
		End If

		source = source.OrderBy(Function(n) n)

		Dim midpoint As Integer = count \ 2
		If count Mod 2 = 0 Then
			Return (Convert.ToDecimal(source.ElementAt(midpoint)) + Convert.ToDecimal(source.ElementAt(midpoint - 1))) / 2.0
		Else
			Return Convert.ToDecimal(source.ElementAt(midpoint))
		End If
	End Function
End Module

